using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using GarageGames.Torque.T2D;
using GarageGames.Torque.Materials;
using GarageGames.Torque.Core;
using Xyne.Templates;
using Xyne.Events;
using Xyne.Utilities;
using Xyne.Templates.Ui.BattleRegion.Bars;
using GarageGames.Torque.Sim;
using Microsoft.Xna.Framework.Input;
using Xyne.Actions;
using Xyne.Templates.BattleEffects;
using Microsoft.Xna.Framework.Net;

namespace Xyne.Regions
{
    class BattleImplEffect
    {
        private BattleEffect effect;
        public BattleEffect Effect
        {
            get { return effect; }
            set { effect = value; }
        }

        private bool valid;
        public bool Valid
        {
            get { return valid; }
            set { valid = value; }
        }
    };

    public class BattleRegionImpl : BattleRegion
    {
        private const int CharacterXOffset = 10;
        private const int CharacterYOffset = 10;

        private Wizard leftWizard;
        private Wizard rightWizard;

        private HealthBar leftHealth;
        private HealthBar rightHealth;

        private List<BattleImplEffect> localEffects;

        public BattleRegionImpl(Rectangle bounds) : base(bounds)
        {
            localEffects = new List<BattleImplEffect>();

            #region ActionManager Callback
            ActionManager.Instance.Callbacks.Add(new ActionManager.ActionStateChangedDelegate(ActionStateChanged));
            #endregion

            InitBackground();


            InitGrass();

            #region Register Player Sprite
            leftWizard = (Wizard)Wizard.Template.Clone();
            leftWizard.WarpToPosition(new Vector2(bounds.Left + (leftWizard.Size.X / 2) + CharacterXOffset, bounds.Bottom - (leftWizard.Size.Y / 2) - CharacterYOffset), 0);
            leftWizard.ObjectType += GetTargetObjectType(GameObjects.LeftWizard);
            TorqueObjectDatabase.Instance.Register(leftWizard);
            leftWizard.FlipX = true;
            leftWizard.SetAnimationFrame(0);
            
            leftWizard.PauseAnimation();
            leftWizard.OnAnimationEnd = new OnAnimationEndDelegate(leftWizard.PartlyLoopAnimation);
            #endregion
            #region Register Enemy Sprite
            rightWizard = (Wizard)Wizard.Template.Clone();
            rightWizard.WarpToPosition(new Vector2(bounds.Right - (leftWizard.Size.X / 2) - CharacterXOffset, bounds.Bottom - (leftWizard.Size.Y / 2) - CharacterYOffset), 0);
            rightWizard.ObjectType += GetTargetObjectType(GameObjects.RightWizard);              
            TorqueObjectDatabase.Instance.Register(rightWizard);
            rightWizard.SetAnimationFrame(0);
            rightWizard.PauseAnimation();            
            rightWizard.OnAnimationEnd = new OnAnimationEndDelegate(rightWizard.PartlyLoopAnimation);
            #endregion

            leftHealth = (HealthBar)HealthBar.Template.Clone();
            leftHealth.FullHeight = bounds.Height;
            leftHealth.Origin = new Vector2(bounds.Left + leftHealth.Size.X / 2, bounds.Height / 2);
            leftHealth.MaxHealth = 100;
            leftHealth.Health = 100;
            TorqueObjectDatabase.Instance.Register(leftHealth);

            rightHealth = (HealthBar)HealthBar.Template.Clone();
            rightHealth.FullHeight = bounds.Height;
            rightHealth.Origin = new Vector2(bounds.Right - rightHealth.Size.X / 2, bounds.Height / 2);
            rightHealth.MaxHealth = 100;
            rightHealth.Health = 100;
            TorqueObjectDatabase.Instance.Register(rightHealth);

            EventManager.Instance.Callbacks.Add(EventReceived);
        }

        public void SetLeftWizardHealth(float health)
        {
            if (health < 0) health = 0;
            if (health > leftHealth.MaxHealth) health = leftHealth.MaxHealth;

            leftHealth.Health = health;

            if (health <= 0 && Game.Network != null)
            {
                if (Game.Network.Wizard == GameObjects.LeftWizard)
                    Game.Network.HandleEvent(new GameLostEvent(this, GameLostEvent.Health));
                else Game.Network.HandleEvent(new GameWonEvent(this, GameWonEvent.EnemyHealth));
            }
        }

        public void SetRightWizardHealth(float health)
        {
            if (health < 0) health = 0;
            if (health > rightHealth.MaxHealth) health = rightHealth.MaxHealth;

            rightHealth.Health = health;

            if (health <= 0 && Game.Network != null)
            {
                if (Game.Network.Wizard == GameObjects.RightWizard)
                    Game.Network.HandleEvent(new GameLostEvent(this, GameLostEvent.Health));
                else Game.Network.HandleEvent(new GameWonEvent(this, GameWonEvent.EnemyHealth));
            }
        }

        public void ModifyHealth(GameObjects target, float healthChange)
        {
            if (target == GameObjects.RightWizard)
            {
                SetRightWizardHealth(rightHealth.Health + healthChange);
            }

            if (target == GameObjects.LeftWizard)
            {
                SetLeftWizardHealth(leftHealth.Health + healthChange);
            }
        }

        public void EventReceived(Event e)
        {
            if (e is StartCastingEvent) // Server only
            {
                StartCastingEvent cast = e as StartCastingEvent;
                if (cast.Gamer == Game.Network.Gamer.Gamertag)
                {
                    if (Game.Network.Wizard == GameObjects.LeftWizard)
                        leftWizard.ResumeAnimation();
                    else
                        rightWizard.ResumeAnimation();
                }
                else
                {
                    if (Game.Network.Wizard == GameObjects.LeftWizard)
                        rightWizard.ResumeAnimation();
                    else
                        leftWizard.ResumeAnimation();
                }
            }
            else if (e is StopCastingEvent) // Server only
            {
                StopCastingEvent cast = e as StopCastingEvent;
                if (cast.Gamer == Game.Network.Gamer.Gamertag)
                {
                    if (Game.Network.Wizard == GameObjects.LeftWizard)
                    {
                        leftWizard.SetAnimationFrame(0);
                        leftWizard.PauseAnimation();
                    }
                    else
                    {
                        rightWizard.SetAnimationFrame(0);
                        rightWizard.PauseAnimation();
                    }
                }
                else
                {
                    if (Game.Network.Wizard == GameObjects.LeftWizard)
                    {
                        rightWizard.SetAnimationFrame(0);
                        rightWizard.PauseAnimation();
                    }
                    else
                    {
                        leftWizard.SetAnimationFrame(0);
                        leftWizard.PauseAnimation();
                    }
                }
            }
        }

        public TorqueObjectType GetTargetObjectType(GameObjects target)
        {
            if (target == GameObjects.LeftWizard)
            {
                return TorqueObjectDatabase.Instance.GetObjectType("leftWizard");
            }
            else
            {
                return TorqueObjectDatabase.Instance.GetObjectType("rightWizard");
            }
        }

        /**
         * State string -> to be moved.
         * 
         * Player's max health
         * Player's health
         * Server's max health
         * Server's health
         * If the player is *not* casting
         * Player's casting animation frame
         * If the server is *not* casting
         * Server's casting animation frame
         * -- Effect List --
         **/
        public override void GetState(PacketWriter writer)
        {
            writer.Write(rightHealth.MaxHealth);
            writer.Write(rightHealth.Health);
            writer.Write(leftHealth.MaxHealth);
            writer.Write(leftHealth.Health);
            writer.Write(rightWizard.AnimationPaused);
            writer.Write(rightWizard.CurrentFrame);
            writer.Write(leftWizard.AnimationPaused);
            writer.Write(leftWizard.CurrentFrame);

            List<BattleEffect> effects = BattleEffectManager.Instance.BattleEffects;
            writer.Write(effects.Count);
            foreach (BattleEffect effect in effects)
            {
                writer.Write(effect.BattleEffectID.ToString());
                writer.Write((ulong)effect.EffectType);
                effect.Serialize(writer);
            }
        }

        public override void SetState(PacketReader reader)
        {
            rightHealth.MaxHealth = reader.ReadSingle();
            rightHealth.Health = reader.ReadSingle();
            leftHealth.MaxHealth = reader.ReadSingle();
            leftHealth.Health = reader.ReadSingle();
            rightWizard.AnimationPaused = reader.ReadBoolean();
            rightWizard.SetAnimationFrame(reader.ReadUInt32());
            leftWizard.AnimationPaused = reader.ReadBoolean();
            leftWizard.SetAnimationFrame(reader.ReadUInt32());

            int numEffects = reader.ReadInt32();
            for (int i = 0; i < localEffects.Count; i++)
            {
                localEffects[i].Valid = false;
            }

            for (int i = 0; i < numEffects; i++)
            {
                String id = reader.ReadString();
                BattleEffect.BattleEffectType type = (BattleEffect.BattleEffectType)reader.ReadUInt64();

                bool found = false;
                for (int e = 0; e < localEffects.Count; e++)
                {
                    if (id == localEffects[e].Effect.BattleEffectID.ToString())
                    {
                        // update object
                        if (localEffects[e].Effect is SplatterEffect)
                        {
                            ((SplatterEffect)localEffects[e].Effect).Update(reader);
                        }
                        else if (localEffects[e].Effect is RepeatBurnEffect)
                        {
                            ((RepeatBurnEffect)localEffects[e].Effect).Update(reader);
                        }
                        else if (localEffects[e].Effect is ThrownFireballEffect)
                        {
                            ((ThrownFireballEffect)localEffects[e].Effect).Update(reader);
                        }
                        else if (localEffects[e].Effect is RepeatBlizzardEffect)
                        {
                            ((RepeatBlizzardEffect)localEffects[e].Effect).Update(reader);
                        }
                        else if (localEffects[e].Effect is ThrownPhoenixEffect) 
                        {
                            ((ThrownPhoenixEffect)localEffects[e].Effect).Update(reader);
                        }
                        else if (localEffects[e].Effect is ThrownTsunamiEffect)
                        {
                            ((ThrownTsunamiEffect)localEffects[e].Effect).Update(reader);
                        }
                        else if (localEffects[e].Effect is RepeatHealingRainEffect)
                        {
                            ((RepeatHealingRainEffect)localEffects[e].Effect).Update(reader);
                        }
                        else if (localEffects[e].Effect is RepeatFloodEffect)
                        {
                            ((RepeatFloodEffect)localEffects[e].Effect).Update(reader);
                        }
                        found = true;
                        localEffects[e].Valid = true;
                    }
                }

                if (!found)
                {
                    // create a new object
                    switch (type)
                    {
                        case BattleEffect.BattleEffectType.SplatterEffect:
                            CreateSplatter(id,reader);
                            break;
                        case BattleEffect.BattleEffectType.RepeatBurnEffect:
                            CreateRepeatBurn(id, reader);
                            break;
                        case BattleEffect.BattleEffectType.ThrownFireballEffect:
                            CreateThrownFireball(id, reader);
                            break;
                        case BattleEffect.BattleEffectType.RepeatBlizzardEffect:
                            CreateBlizzard(id, reader);
                            break;
                        case BattleEffect.BattleEffectType.ThrownPhoenixEffect:
                            CreatePhoenix(id, reader);
                            break;
                        case BattleEffect.BattleEffectType.ThrownTsunamiEffect:
                            CreateTsunami(id, reader);
                            break;
                        case BattleEffect.BattleEffectType.RepeatHealingRainEffect:
                            CreateHealingRain(id, reader);
                            break;
                        case BattleEffect.BattleEffectType.RepeatFloodEffect:
                            CreateFlood(id, reader);
                            break;
                    }
                }
            }

            for (int i = 0; i < localEffects.Count; i++)
            {
                if (!localEffects[i].Valid)
                {
                    TorqueObjectDatabase.Instance.Unregister(localEffects[i].Effect);
                    localEffects.RemoveAt(i);
                    if (i > 0) i--;
                }
            }
        }

        #region Create Functions
        private void CreateFlood(string id, PacketReader reader)
        {
            RepeatFloodEffect healingRain = (RepeatFloodEffect)RepeatFloodEffect.Template.Clone();
            healingRain.BattleEffectID = new Guid(id);

            healingRain.Update(reader);

            TorqueObjectDatabase.Instance.Register(healingRain);

            BattleImplEffect effect = new BattleImplEffect();
            effect.Effect = healingRain;
            effect.Valid = true;
            localEffects.Add(effect);
        }

        private void CreateHealingRain(string id, PacketReader reader)
        {
            RepeatHealingRainEffect healingRain = (RepeatHealingRainEffect)RepeatHealingRainEffect.Template.Clone();
            healingRain.BattleEffectID = new Guid(id);

            healingRain.Update(reader);

            TorqueObjectDatabase.Instance.Register(healingRain);

            BattleImplEffect effect = new BattleImplEffect();
            effect.Effect = healingRain;
            effect.Valid = true;
            localEffects.Add(effect);
        }

        private void CreateTsunami(string id, PacketReader reader)
        {
            ThrownTsunamiEffect tsunami = (ThrownTsunamiEffect)ThrownTsunamiEffect.Template.Clone();
            tsunami.BattleEffectID = new Guid(id);

            tsunami.Update(reader);

            TorqueObjectDatabase.Instance.Register(tsunami);

            BattleImplEffect effect = new BattleImplEffect();
            effect.Effect = tsunami;
            effect.Valid = true;
            localEffects.Add(effect);
        }

        private void CreatePhoenix(string id, PacketReader reader)
        {
            ThrownPhoenixEffect phoenix = (ThrownPhoenixEffect)ThrownPhoenixEffect.Template.Clone();
            phoenix.BattleEffectID = new Guid(id);

            phoenix.Update(reader);

            TorqueObjectDatabase.Instance.Register(phoenix);

            BattleImplEffect effect = new BattleImplEffect();
            effect.Effect = phoenix;
            effect.Valid = true;
            localEffects.Add(effect);
        }

        private void CreateBlizzard(string id, PacketReader reader)
        {
            RepeatBlizzardEffect blizzard = (RepeatBlizzardEffect)RepeatBlizzardEffect.Template.Clone();
            blizzard.BattleEffectID = new Guid(id);

            blizzard.Update(reader);

            TorqueObjectDatabase.Instance.Register(blizzard);
            blizzard.StartEffect();

            BattleImplEffect effect = new BattleImplEffect();
            effect.Effect = blizzard;
            effect.Valid = true;
            localEffects.Add(effect);
        }

        private void CreateSplatter(String id, PacketReader reader)
        {
            SplatterEffect splatter = (SplatterEffect)SplatterEffect.Template.Clone();
            splatter.BattleEffectID = new Guid(id);

            splatter.Update(reader);

            TorqueObjectDatabase.Instance.Register(splatter);
            splatter.StartEffect();

            BattleImplEffect effect = new BattleImplEffect();
            effect.Effect = splatter;
            effect.Valid = true;
            localEffects.Add(effect);
        }

        private void CreateRepeatBurn(String id, PacketReader reader)
        {
            RepeatBurnEffect repeat = (RepeatBurnEffect)RepeatBurnEffect.Template.Clone();
            repeat.BattleEffectID = new Guid(id);

            repeat.Update(reader);

            TorqueObjectDatabase.Instance.Register(repeat);
            repeat.Constant = true;
            repeat.StartEffect();

            BattleImplEffect effect = new BattleImplEffect();
            effect.Effect = repeat;
            effect.Valid = true;
            localEffects.Add(effect);
        }

        private void CreateThrownFireball(String id, PacketReader reader)
        {
            ThrownFireballEffect fireball = (ThrownFireballEffect)ThrownFireballEffect.Template.Clone();
            fireball.BattleEffectID = new Guid(id);

            fireball.Update(reader);

            TorqueObjectDatabase.Instance.Register(fireball);
            fireball.Init();

            BattleImplEffect effect = new BattleImplEffect();
            effect.Effect = fireball;
            effect.Valid = true;
            localEffects.Add(effect);
        }
        #endregion

        public Vector2 GetPosition(GameObjects target)
        {
            switch (target)
            {
                case GameObjects.LeftWizard:
                    return leftWizard.Position;
                case GameObjects.RightWizard:
                    return rightWizard.Position;
                default:
                    return Vector2.Zero;
            }
        }

        #region Private Methods
        private void ActionStateChanged(Action a)
        {
            switch (a.Type)
            {
                case ActionTypes.DirectHealth:
                    DirectHealthAction action = a as DirectHealthAction;
                    
                    foreach (GameObjects target in action.ActualTargets)
                    {
                        ModifyHealth(target, action.HealthChange);
                        SplatterEffect splatter = (SplatterEffect)SplatterEffect.Template.Clone();
                        splatter.Init(action);
                        splatter.WarpToPosition(GetPosition(target), 0.0f);
                        BattleEffectManager.Instance.AddBattleEffect(splatter);
                    }
                    break;
                case ActionTypes.RepeatHealth:
                    RepeatHealthAction repeatHealthAction = a as RepeatHealthAction;
                    switch (repeatHealthAction.Effect)
                    {
                        case RepeatHealthEffects.Burn:
                            foreach (GameObjects target in repeatHealthAction.ActualTargets)
                            {

                                RepeatBurnEffect myEffect = (RepeatBurnEffect)RepeatBurnEffect.Template.Clone();
                                myEffect.WarpToPosition(GetPosition(target), 0.0f);
                                myEffect.Init(repeatHealthAction, this, target);
                                BattleEffectManager.Instance.AddBattleEffect(myEffect);
                            }
                            break;
                        case RepeatHealthEffects.Blizzard:
                            foreach (GameObjects target in repeatHealthAction.ActualTargets)
                            {

                                RepeatBlizzardEffect myEffect = (RepeatBlizzardEffect)RepeatBlizzardEffect.Template.Clone();
                                myEffect.WarpToPosition(GetPosition(target) + new Vector2(-10,-30), 0.0f);
                                myEffect.Init(repeatHealthAction, this, target);
                                BattleEffectManager.Instance.AddBattleEffect(myEffect);
                            }
                            break;
                        case RepeatHealthEffects.HealingRain:
                            foreach (GameObjects target in repeatHealthAction.ActualTargets)
                            {
                                RepeatHealingRainEffect myEffect = (RepeatHealingRainEffect)RepeatHealingRainEffect.Template.Clone();
                                myEffect.WarpToPosition(GetPosition(target) + new Vector2(0, -30), 0.0f);
                                myEffect.Init(repeatHealthAction, this, target);
                                BattleEffectManager.Instance.AddBattleEffect(myEffect);
                            }
                            break;
                        case RepeatHealthEffects.Flood:
                            foreach (GameObjects target in repeatHealthAction.ActualTargets)
                            {
                                RepeatFloodEffect myEffect = (RepeatFloodEffect)RepeatFloodEffect.Template.Clone();
                                myEffect.WarpToPosition(new Vector2(Bounds.Width/2, Bounds.Height-25), 0.0f);
                                myEffect.Init(repeatHealthAction, this, target);
                                BattleEffectManager.Instance.AddBattleEffect(myEffect);
                            }
                            break;
                    }

                    break;
                case ActionTypes.Projectile:
                    ProjectileAction thrownAction = a as ProjectileAction;
                    switch (thrownAction.Effect)
                    {
                        case ProjectileEffects.Fireball:
                            foreach (GameObjects target in thrownAction.ActualTargets)
                            {
                                ThrownFireballEffect myFireballEffect = (ThrownFireballEffect)ThrownFireballEffect.Template.Clone();
                                myFireballEffect.Init(thrownAction, this, target);
                                BattleEffectManager.Instance.AddBattleEffect(myFireballEffect);
                            }
                            break;
                        case ProjectileEffects.Phoenix:
                            foreach (GameObjects target in thrownAction.ActualTargets)
                            {
                                ThrownPhoenixEffect myPhoenixEffect = (ThrownPhoenixEffect)ThrownPhoenixEffect.Template.Clone();
                                myPhoenixEffect.Init(thrownAction, this, target);
                                BattleEffectManager.Instance.AddBattleEffect(myPhoenixEffect);
                            }
                            break;
                        case ProjectileEffects.Tsunami:
                            foreach (GameObjects target in thrownAction.ActualTargets)
                            {
                                ThrownTsunamiEffect myTsunamiEffect = (ThrownTsunamiEffect)ThrownTsunamiEffect.Template.Clone();
                                myTsunamiEffect.Init(thrownAction, this, target);
                                BattleEffectManager.Instance.AddBattleEffect(myTsunamiEffect);
                            }
                            break;

                    }

                    break;
            }
        }

        #region Init Methods
        private void InitBackground()
        {
            T2DSceneObject regionBackground = new T2DSceneObject();
            T2DStaticSprite background = new T2DStaticSprite();
            background.Size = new Vector2(Bounds.Width, Bounds.Height);
            background.Position = new Vector2(Bounds.Left + (Bounds.Width / 2), Bounds.Top + (Bounds.Height / 2));
            background.Layer = Game.BackgroundLayer;
            SimpleMaterial material = new SimpleMaterial();
            material.TextureFilename = @"data\images\battleregion.PNG";
            background.Material = material;
            TorqueObjectDatabase.Instance.Register(background);
        }

        private void InitGrass()
        {
            T2DSceneObject grassObject = new T2DSceneObject();
            T2DStaticSprite grass = new T2DStaticSprite();
            grass.Size = new Vector2(Bounds.Width, Bounds.Height / 5);
            grass.Position = new Vector2(Bounds.Left + Bounds.Width / 2, Bounds.Top + Bounds.Height - (Bounds.Height / 10));
            grass.Layer = Game.GrassLayer;
            SimpleMaterial material = new SimpleMaterial();
            material.TextureFilename = @"data\images\ui\battleregion\grass.png";
            grass.Material = material;
            TorqueObjectDatabase.Instance.Register(grass);
        }
        #endregion
        #endregion
    }
}
